using System;
using System.Text;
using System.IO;
using System.Web;
using System.Collections;
using System.Reflection;

using Microsoft.Win32;
using System.Xml.Linq;
using System.Xml;
using System.Collections.Generic;
using System.Drawing;
using System.Net;
using HttpServer;

namespace GestureListenerClient.Services.HTTP
{
	/// <summary>
	/// Summary description for MyServer.
	/// </summary>
	public class HTTPServer
	{
        // Gesture node relations in the XML document
        private static string GESTURE_NODE = "gesture";
        private static string GESTURE_ID_ATTR = "id";
        private static string GESTURE_INDEX_ATTR = "index";
        private static string GESTURE_TYPE_ATTR = "type";

        private static string GESTURE_TIME_ATTR = "time";

        private static string COORD_LIST_NODE = "coordinates";

        private static string COORD_NODE = "coordinate";
        private static string COORD_TYPE_ATTR = "type";
        private static string COORD_TYPE_CENTER = "CENTER";
        private static string COORD_TYPE_CONTACT = "CONTACT";
        private static string COORD_X_ATTR = "x";
        private static string COORD_Y_ATTR = "y";

        //TODO: Pass by reference
        private GestureClient gestClient;
        private HttpServer.HttpListener listener;

        /// <summary>
        /// Create a HTTP server with a specific gesture client to report back to
        /// </summary>
        /// <param name="gc">Gesture Client to call when new gestures occur</param>
        public HTTPServer(GestureClient gc) : this(gc, 8080) { }

        /// <summary>
        /// Create a HTTP server with a specific gesture client to report back to
        /// </summary>
        /// <param name="gc">Gesture Client to call when new gestures occur</param>
        /// <param name="thePort">Port to open listening on</param>
        public HTTPServer(GestureClient gc, int thePort)
        {
            gestClient = gc;
            listener = HttpServer.HttpListener.Create(IPAddress.Any, thePort);
            listener.RequestReceived += this.OnRequest;

            
            this.ServerPath = new Uri("http://" + GetMyIP() + ":" + thePort);
           
        }

        public static string GetMyIP()
        {

            string myHost = System.Net.Dns.GetHostName();
            string myIPv4 = String.Empty;

            System.Net.IPHostEntry myIPs = System.Net.Dns.GetHostEntry(myHost);

            // Loop through all IP addresses and display each 

            foreach (System.Net.IPAddress myIP in myIPs.AddressList)
            {

                if (!myIP.AddressFamily.ToString().Equals("InterNetworkV6")
                    && !myIP.ToString().Substring(0, 3).Equals("169"))
                {
                    return myIP.ToString();
                }


            }

            return "localhost";

        }
        public Uri ServerPath
        {
            get;
            private set;
        }
        /// <summary>
        /// Start listening for incoming gesture alerts
        /// </summary>
        public void startListening()
        {
            startListening(10);
        }

        /// <summary>
        /// Start listening for incoming gesture alerts
        /// </summary>
        /// <param name="queueSize">Size of the queue for backlogged incoming http messages (gestures)</param>
        public void startListening(int queueSize)
        {
            this.listener.Start(queueSize);
        }

        /// <summary>
        /// Stop listening for incoming gesture alerts
        /// </summary>
        public void stopListening()
        {
            this.listener.Stop();
        }

        /// <summary>
        /// Handle an incoming http request
        /// </summary>
        /// <param name="sender">HTTPClientContext sender</param>
        /// <param name="e">Contains request information</param>
        private void OnRequest(object sender, RequestEventArgs e)
        {
            if (e.Request.Method.ToUpper() == "POST")
            {
                //Read the incoming request body (our xml of gestures)
                StreamReader streamRead = new StreamReader(e.Request.Body);
                string gestureText = streamRead.ReadToEnd();

                //Parse into gestures and alert our listener
                Gestures.Gesture[] gestures = parseXML(gestureText);
                foreach (Gestures.Gesture gesture in gestures)
                {
                    gestClient.gestureOccurred(gesture);
                }

                //Send the OK response we got gestures
                if (sender is IHttpClientContext)
                {
                    HttpServer.HttpResponse resp = new HttpServer.HttpResponse(sender as HttpClientContext, e.Request);
                    resp.Connection = ConnectionType.Close;
                    resp.Status = HttpStatusCode.OK;

                    byte[] buffer = Encoding.UTF8.GetBytes("Successful parse. " + gestures.Length + " gestures accepted");
                    resp.Body.Write(buffer, 0, buffer.Length);
                    resp.Send();
                }
            }
            else
            {

                if (sender is IHttpClientContext)
                {
                    HttpServer.HttpResponse resp = new HttpServer.HttpResponse(sender as HttpClientContext, e.Request);
                    resp.Connection = ConnectionType.Close;
                    resp.Status = HttpStatusCode.OK;

                    byte[] buffer = Encoding.UTF8.GetBytes("HTTP server is still alive");
                    resp.Body.Write(buffer, 0, buffer.Length);
                    resp.Send();
                }
            }
        }
        /// <summary>
        /// Parse a formatted xml document of gestures
        /// </summary>
        /// <param name="xmlGests">xml to parse</param>
        /// <returns>Array of parsed gesture objects</returns>
        private Gestures.Gesture[] parseXML(string xmlGests)
        {
            List<Gestures.Gesture> gestList = new List<GestureListenerClient.Gestures.Gesture>();
            //LINQ to break xml into readable nodes
            var xDoc = XElement.Parse(xmlGests).Elements(GESTURE_NODE);
            foreach (var gestElem in xDoc)
            {
                gestList.Add(parseGesture(gestElem));
            }
            return gestList.ToArray();
        }

        private Gestures.Gesture parseGesture(XElement gestElem)
        {
            //Get the gesture type
            string gestTypeStr = gestElem.Attribute(GESTURE_TYPE_ATTR).Value;
            //Use map to see what gesture class to instantiate
            Type gestType = GestureReflection.GESTURE_CLASS_MAP[gestTypeStr];
            if (gestType != null)
            {
                int id = -1;
                if (gestElem.Attribute(GESTURE_ID_ATTR) != null) id = int.Parse(gestElem.Attribute(GESTURE_ID_ATTR).Value);

                //index on screen
                int index = -1;
                if (gestElem.Attribute(GESTURE_INDEX_ATTR) != null) index = int.Parse(gestElem.Attribute(GESTURE_INDEX_ATTR).Value);

                //timestamp gesture occurred
                DateTime timestamp = DateTime.Parse(gestElem.Attribute(GESTURE_TIME_ATTR).Value);

                //Get the coordinates of the gesture
                List<PointF> coords = new List<PointF>();
                PointF center = PointF.Empty;
                foreach (var gestCoord in gestElem.Elements(COORD_LIST_NODE).Elements(COORD_NODE))
                {
                    if (gestCoord.Attribute(COORD_TYPE_ATTR).Value.Equals(COORD_TYPE_CONTACT, StringComparison.OrdinalIgnoreCase))
                    {
                        float x = float.Parse(gestCoord.Attribute(COORD_X_ATTR).Value);
                        float y = float.Parse(gestCoord.Attribute(COORD_Y_ATTR).Value);
                        coords.Add(new PointF { X = x, Y = y });
                    }
                    else if (gestCoord.Attribute(COORD_TYPE_ATTR).Value.Equals(COORD_TYPE_CENTER, StringComparison.OrdinalIgnoreCase))
                    {
                        float x = float.Parse(gestCoord.Attribute(COORD_X_ATTR).Value);
                        float y = float.Parse(gestCoord.Attribute(COORD_Y_ATTR).Value);
                        center = new PointF { X = x, Y = y };
                    }
                }

                //Create the gesture using reflection
                Gestures.Gesture gestToAdd = Activator.CreateInstance(gestType, new object[] { id, index, timestamp, coords.ToArray() }) as Gestures.Gesture;
                if (gestToAdd != null) gestToAdd.Center = center;

                return gestToAdd;
            }
            else throw new System.FormatException("Invalid gesture type recognized. " + gestTypeStr + " is not a valid gesture");
        }
	}
}
